﻿#include "HnrjitDialog.h"
#include "HnrjitContainer.h"
#include "HnrjitControl.h"
#include "HnrjitImaging.h"

static HMODULE g_hInstance; // 当前DLL实例句柄
static CRITICAL_SECTION g_csMapDialogs; // 全局同步对象
static CAtlMap<HWND, CHnrjitDialog*> g_mapDialogs; // 对话框数组

extern "C" BOOL APIENTRY DllMain(HANDLE hInstance, DWORD dwReason, LPVOID lpReserved)
{
	UNREFERENCED_PARAMETER(lpReserved);

	if (dwReason == DLL_PROCESS_ATTACH)
	{
		g_hInstance = (HMODULE) hInstance;
	}
	return TRUE;
}

CHnrjitDialog::CHnrjitDialog()
	: CHnrjitWigdet()
	, m_hWnd(NULL)
	, m_hWndInsertAfter(NULL)
	, m_bModal(FALSE)
	, m_pFocusCantainer(NULL)
{
	m_hTemplate = GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, sizeof(DLGTEMPLATE));
}

CHnrjitDialog::~CHnrjitDialog()
{
	if (m_hTemplate) GlobalFree(m_hTemplate);
}

void CHnrjitDialog::SetAttribute(const TiXmlElement* pxElement, HZIP hZipRes /*= NULL*/)
{
	CHnrjitWigdet::SetAttribute(pxElement, hZipRes);

	const CHAR* pszValue;
	if (pszValue = pxElement->Attribute("Caption"))
	{
		CAtlArray<TCHAR> caResult;
		const TCHAR* pszCaption = CHnrjitImaging::Utf8ToLocal(pszValue, &caResult);
		if (pszCaption) SetCaption(pszCaption);
	}
	if (pszValue = pxElement->Attribute("Topmost"))
	{
		if (strcmp(pszValue, "true") == 0)
		{
			SetInsertAfter(HWND_TOPMOST);
		}
	}
}

const CAtlString&  CHnrjitDialog::GetCaption() const
{
	return m_strCaption;
}

void CHnrjitDialog::SetCaption(const TCHAR* pszCaption)
{
	if (m_strCaption != pszCaption)
	{
		m_strCaption = pszCaption;
	}
	if (IsWindow())
	{
		SetWindowText(pszCaption);
	}
}

void CHnrjitDialog::SetLocation(const CPoint& ptLocation)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (GetLocation() != ptLocation)
	{
		m_rcGeometry.MoveToXY(ptLocation);
	}
	if (IsWindow())
	{
		SetWindowPos(m_hWndInsertAfter, ptLocation.x, ptLocation.y, 0, 0, SWP_NOSIZE);
	}
}

void CHnrjitDialog::SetLocation(int nX, int nY)
{
	SetLocation(CPoint(nX, nY));
}

void CHnrjitDialog::SetSize(const CSize& tagSize)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (GetSize() != tagSize)
	{
		m_rcGeometry = CRect(GetLocation(), tagSize);
	}
	if (IsWindow())
	{
		SetWindowPos(m_hWndInsertAfter, 0, 0, tagSize.cx, tagSize.cy, SWP_NOMOVE);
	}
}

void CHnrjitDialog::SetSize(int nWidth, int nHeight)
{
	SetSize(CSize(nWidth, nHeight));
}

void CHnrjitDialog::SetGeometry(CPoint ptLocation, CSize tagSize)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	CRect rcGeometry = CRect(ptLocation, tagSize);
	if (m_rcGeometry != rcGeometry)
	{
		m_rcGeometry = rcGeometry;
	}
	if (IsWindow())
	{
		SetWindowPos(m_hWndInsertAfter, ptLocation.x, ptLocation.y, tagSize.cx, tagSize.cy, 0);
	}
}

void CHnrjitDialog::SetGeometry(int nX, int nY, int nWidth, int nHeight)
{
	SetGeometry(CPoint(nX, nY), CSize(nWidth, nHeight));
}

BOOL CHnrjitDialog::IsEnabled() const
{
	return IsWindowEnabled();
}

void CHnrjitDialog::SetEnabled(BOOL isEnabled)
{
	ATLASSERT(m_dwThreadID == GetCurrentThreadId());
	if (m_isEnabled != isEnabled)
	{
		m_isEnabled = isEnabled;
	}
	if (IsWindow())
	{
		EnableWindow(isEnabled);
	}
}

BOOL CHnrjitDialog::IsVisible() const
{
	return IsWindowVisible();
}

void CHnrjitDialog::SetVisible(BOOL isVisible)
{
	if (m_isVisible != isVisible)
	{
		m_isVisible = isVisible;
	}
	if (IsWindow())
	{
		ShowWindow(isVisible ? SW_SHOW : SW_HIDE);
	}
}

void CHnrjitDialog::Invalidate(const CRect& rcRect)
{
	if (GetSafeHwnd())
	{
		InvalidateRect(rcRect, FALSE);
	}
}

HWND CHnrjitDialog::GetInsertAfter() const
{
	return m_hWndInsertAfter;
}

void CHnrjitDialog::SetInsertAfter(HWND hWndInsertAfter)
{
	if (m_hWndInsertAfter != hWndInsertAfter)
	{
		m_hWndInsertAfter = hWndInsertAfter;
	}
	if (IsWindow())
	{
		SetWindowPos(m_hWndInsertAfter, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
	}
}

void CHnrjitDialog::DrawDailog(HDC hdc)
{
	CRect rcClipBox;
	GetClipBox(hdc, rcClipBox);

	HDC hdcMemory = CreateCompatibleDC(hdc);
	if (hdcMemory)
	{
		CSize m_tagSize = GetSize();
		HBITMAP hNewBitmap = CreateCompatibleBitmap(hdc, m_tagSize.cx, m_tagSize.cy);
		if (hNewBitmap)
		{
			HGDIOBJ hOldBitmap = SelectObject(hdcMemory, hNewBitmap);
			SetBkMode(hdcMemory, TRANSPARENT);

			for (size_t i = 0; i < m_arrContainers.GetCount(); i++)
			{
				m_arrContainers[i]->DrawClipBox(hdcMemory, rcClipBox);
			}

			BitBlt(hdc, 0, 0, m_tagSize.cx, m_tagSize.cy, hdcMemory, 0, 0, SRCCOPY);

			SelectObject(hdcMemory, hOldBitmap);
			DeleteObject(hNewBitmap);
		}
		DeleteDC(hdcMemory);
	}
}

LRESULT CHnrjitDialog::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	ATLASSERT(m_hWnd != NULL);
	switch (message)
	{
	case WM_MOVE:
		SetLocation(CPoint(LOWORD(lParam), HIWORD(lParam)));
		return TRUE;
	case WM_SIZE:
		SetSize(CSize(LOWORD(lParam), HIWORD(lParam)));
		return TRUE;
	case WM_ERASEBKGND:
		return TRUE;
	case WM_PAINT:
		{
			HDC hdcDialog = BeginPaint(&m_PaintStruct);
			if (hdcDialog)
			{
				DrawDailog(hdcDialog);
				EndPaint(&m_PaintStruct);
			}
		}
		return TRUE;
	case WM_KEYDOWN: // 等需要使用时细化处理
		OnKeyPress(wParam, lParam & 0xffff, (lParam >> 16) & 0xffff);
		return TRUE;
	case WM_KEYUP: // 等需要使用时细化处理
		OnKeyRelease(wParam, lParam & 0xffff, (lParam >> 16) & 0xffff);
		return TRUE;
	case WM_LBUTTONDOWN:
		{
			SetCapture(); // 捕获本窗口之外鼠标事件
			CPoint point(LOWORD(lParam), HIWORD(lParam));
			OnMousePress(wParam, point);
		}
		return TRUE;
	case WM_LBUTTONUP:
		{
			ReleaseCapture(); // 释放本窗口之外鼠标事件
			CPoint point(LOWORD(lParam), HIWORD(lParam));
			OnMouseRelease(wParam, point);
		}
		return TRUE;
	case WM_MOUSEMOVE:
		if (wParam != 0)
		{
			if (GetCursor()) SetCursor(NULL);
			CPoint point(LOWORD(lParam), HIWORD(lParam));
			OnMouseMove(wParam, point);
		}
		return TRUE;
	case WM_COMMAND:
		OnCommand(wParam, lParam);
		return TRUE;
	case WM_TIMER:
		OnTimer(wParam);
		return TRUE;
	default:
		break;
	}

	return FALSE;
}

UINT CHnrjitDialog::AddContainer(CHnrjitContainer* pContainer)
{
	for (size_t i = 0; i < m_arrContainers.GetCount(); i++)
	{
		if (m_arrContainers[i] == pContainer)
		{
			// 容器已添加过
			return (UINT)i;
		}
	}

	return m_arrContainers.Add(pContainer);
}

BOOL CHnrjitDialog::RemoveContainer(CHnrjitContainer* pContainer)
{
	for (size_t i = 0; i < m_arrContainers.GetCount(); i++)
	{
		if (m_arrContainers[i] == pContainer)
		{
			m_arrContainers.RemoveAt(i);
			return TRUE;
		}
	}
	return FALSE;
}

void CHnrjitDialog::OnKeyPress(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (m_pFocusCantainer && m_pFocusCantainer->IsVisible() && m_pFocusCantainer->IsEnabled())
	{
		m_pFocusCantainer->OnKeyPress(nChar, nRepCnt, nFlags);
	}
}

void CHnrjitDialog::OnKeyRelease(UINT nChar, UINT nRepCnt, UINT nFlags)
{
	if (m_pFocusCantainer && m_pFocusCantainer->IsVisible() && m_pFocusCantainer->IsEnabled())
	{
		m_pFocusCantainer->OnKeyRelease(nChar, nRepCnt, nFlags);
	}
}

void CHnrjitDialog::OnMousePress(UINT nFlags, CPoint point)
{
	for (int i = m_arrContainers.GetCount() - 1; i >= 0; i--)
	{
		// 从最后一个容器响应起
		if (m_arrContainers[i]->IsVisible() && !m_arrContainers[i]->IsTransparent())
		{
			// 容器可见，非透明
			if (m_arrContainers[i]->GetGeometry().PtInRect(point))
			{
				if (m_arrContainers[i]->IsEnabled())
				{
					// 容器可用
					if (m_pFocusCantainer == m_arrContainers[i])
					{
						// 按下的控件之前按下过
						m_pFocusCantainer->OnMousePress(nFlags, point);
						return;
					}
					if (m_pFocusCantainer)
					{
						m_pFocusCantainer->OnMouseRelease(nFlags, point);
					}
					m_pFocusCantainer = m_arrContainers[i];
					m_arrContainers[i]->OnMousePress(nFlags, point);
				}
				break;
			}
		}
	}
}

void CHnrjitDialog::OnMouseRelease(UINT nFlags, CPoint point)
{
	for (int i = m_arrContainers.GetCount() - 1; i >= 0; i--)
	{
		// 从最后一个容器响应起
		if (m_arrContainers[i]->IsVisible() && !m_arrContainers[i]->IsTransparent())
		{
			// 容器可见，可用，非透明
			if (m_arrContainers[i]->GetGeometry().PtInRect(point))
			{
				if (m_arrContainers[i]->IsEnabled())
				{
					if (m_pFocusCantainer == m_arrContainers[i])
					{
						// 释放的控件之前按下过
						m_pFocusCantainer->OnMouseRelease(nFlags, point);
						return;
					}
				}
				break;
			}
		}
	}
	if (m_pFocusCantainer)
	{
		// 之前按下的控件不是当前释放的控件或没有按下任何控件
		m_pFocusCantainer->OnMouseRelease(nFlags, point);
	}
}

void CHnrjitDialog::OnMouseMove(UINT nFlags, CPoint point)
{
	if (m_pFocusCantainer)
	{
		m_pFocusCantainer->OnMouseMove(nFlags, point);
	}
}

BOOL CHnrjitDialog::OnCommand(WPARAM wParam, LPARAM lParam)
{
	int idButton = LOWORD(wParam);
	if (idButton == IDOK || idButton == IDCANCEL)
	{
		if (m_bModal)
		{
			EndDialog(idButton);
			return TRUE;
		}
	}
	return FALSE;
}

void CHnrjitDialog::OnTimer(UINT_PTR nIDEvent)
{

}

BOOL CHnrjitDialog::OnInitDialog()
{
	return TRUE;
}

INT_PTR CHnrjitDialog::DoModal(HWND hWndParent /*= GetActiveWindow()*/)
{
	ATLASSERT(m_hWnd == NULL);

	m_bModal = TRUE; // 模态对话框
	return DialogBoxIndirectParam(g_hInstance, (DLGTEMPLATE*) m_hTemplate, hWndParent, DialogProc, (LPARAM) this);;
}

BOOL CHnrjitDialog::EndDialog(int nRetCode)
{
	ATLASSERT(::IsWindow(m_hWnd));
#ifdef _DEBUG
	ATLASSUME(m_bModal);	// must be a modal dialog
#endif //_DEBUG
	return ::EndDialog(m_hWnd, nRetCode);
}

BOOL CHnrjitDialog::Create(HWND hWndParent /*= NULL*/)
{
	ATLASSERT(m_hWnd == NULL);
	HWND hWnd = CreateDialogIndirectParam(g_hInstance, (DLGTEMPLATE*) m_hTemplate, hWndParent, DialogProc, (LPARAM) this);
	return m_hWnd == hWnd;
}

BOOL CHnrjitDialog::DestroyWindow()
{
	ATLASSERT(::IsWindow(m_hWnd));
#ifdef _DEBUG
	ATLASSERT(!m_bModal);	// must not be a modal dialog
#endif //_DEBUG
	return ::DestroyWindow(m_hWnd);
}

BOOL CALLBACK CHnrjitDialog::DialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	if (uMsg)
	{
		if (uMsg == WM_INITDIALOG)
		{
			CHnrjitDialog* pThis;

			ATLASSERT(!g_mapDialogs.Lookup(hwndDlg, pThis));

			pThis = (CHnrjitDialog*) lParam;

			pThis->m_hWnd = hwndDlg;
			pThis->SetCaption(pThis->m_strCaption);
			if (pThis->m_bModal)
			{
				CRect rcClient;
				CPoint ptLocation;
				HWND hWndParent = pThis->GetParent();
				if (hWndParent)
				{
					::GetClientRect(hWndParent, rcClient);
				}
				else
				{
					::GetClientRect(GetDesktopWindow(), rcClient);
				}
				ptLocation.x = (rcClient.Width() - pThis->GetSize().cx) / 2;
				ptLocation.y = (rcClient.Height() - pThis->GetSize().cy) / 2;
				pThis->SetGeometry(ptLocation, pThis->GetSize());
			}
			else
			{
				pThis->SetGeometry(pThis->GetLocation(), pThis->GetSize());
			}
			pThis->SetEnabled(pThis->m_isEnabled);
			pThis->SetVisible(pThis->m_isVisible);

			g_mapDialogs.SetAt(hwndDlg, pThis);

			return pThis->OnInitDialog();
		}

		CHnrjitDialog* pThis;
		if (g_mapDialogs.Lookup(hwndDlg, pThis))
		{
			if (uMsg == WM_DESTROY)
			{
				pThis->m_hWnd = NULL;
				return (BOOL) g_mapDialogs.RemoveKey(hwndDlg);
			}

			return pThis->WindowProc(uMsg, wParam, lParam);
		}
	}

	return FALSE;
}
